package com.weiiew.backend.operationUtils;

import com.weiiew.backend.entity.enums.ExecutorState;
import com.weiiew.backend.operation.*;
import com.weiiew.backend.utils.HotkeyRepository;
import com.weiiew.backend.utils.listener.ListenerManager;
import lombok.extern.slf4j.Slf4j;
import org.jnativehook.keyboard.NativeKeyAdapter;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputAdapter;
import org.jnativehook.mouse.NativeMouseWheelEvent;
import org.jnativehook.mouse.NativeMouseWheelListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author 魏锴
 * @since 2025/4/26
 */
@Slf4j
@Component
public class OperationRecorder {
    // 该类中只有RUNNING / PAUSE / COUNTDOWN 这三个状态
    private final OperationSequence sequence = new OperationSequence();
    @Autowired
    private ListenerManager manager;
    // 在类中定时器使用的参数
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final AtomicInteger countdown = new AtomicInteger(4); // 倒计时3秒
    private final AtomicReference<ExecutorState> state = new AtomicReference<>(ExecutorState.PAUSE);
    @Autowired
    private HotkeyRepository repository;
    private ScheduledFuture<?> countdownTask;
    private long recordingStartTime = 0;
    private long timeOffset = 0;

    private RecorderCommunicationTool tool = new RecorderCommunicationTool();

    @PostConstruct
    private void init() {
        // 初始化, 添加各种监听器
        manager.registerKeyListener("operation-key", new KeyListener());
        manager.registerMouseListener("operation-mouse", new MouseListener());
        manager.registerMouseWheelListener("operation-wheel", new MouseWheelListener());
    }

    @PreDestroy
    private void destroy() {
        // 销毁
        // 释放监听器资源
        manager.unregisterKeyListener("operation-key");
        manager.unregisterMouseListener("operation-mouse");
        manager.unregisterMouseWheelListener("operation-wheel");
        // 关闭定时器
        scheduler.shutdown();
        if (countdownTask != null) {
            countdownTask.cancel(true);
        }
    }

    public void setLogger(SimpMessagingTemplate template, String channel) {
        this.tool = new RecorderCommunicationTool(template, channel);
    }

    public OperationSequence getRecordedSequence() {
        return sequence;
    }    // 时间戳处理方法

    private long getTimestamp() {
        return System.currentTimeMillis() - recordingStartTime;
    }

    public ExecutorState getState() {
        return state.get();
    }


    /**
     * 开始倒计时
     */
    private void startCountdown() {
        // 先重置倒计时状态
        countdown.set(4);
        state.set(ExecutorState.COUNTDOWN);
        // 每秒更新倒计时
        countdownTask = scheduler.scheduleAtFixedRate(() -> {
            int remaining = countdown.decrementAndGet();
            if (remaining > 0) {
                tool.sendMessage(state.get(), sequence.size(), "开始录制倒计时: {} 秒", remaining);
            } else {
                // 倒计时结束，设置状态并清理
                recordingStartTime = System.currentTimeMillis(); // 重置时间
                state.set(ExecutorState.RUNNING);
                tool.sendMessage(state.get(), sequence.size(), "开始录制!");
                countdownTask.cancel(true); // 停止倒计时任务
            }
        }, 0, 1, TimeUnit.SECONDS); // 立即开始，每秒执行一次
    }

    /**
     * 启动记录
     */
    public void start() {
        synchronized (state) {
            if (state.get() == ExecutorState.RUNNING || state.get() == ExecutorState.COUNTDOWN) {
                throw new IllegalStateException("操作录制已开始");
            }
            // 计算开始时间
            recordingStartTime = System.currentTimeMillis() - timeOffset;
            // 重置操作队列
            sequence.clearOperations();
            // 启动倒计时
            startCountdown();
        }
    }

    /**
     * 停止记录
     */
    public void pause() {
        synchronized (state) {
            if (state.get() == ExecutorState.PAUSE) throw new IllegalStateException("操作录制已暂停");
            state.set(ExecutorState.PAUSE);
            if (countdownTask != null) countdownTask.cancel(true);
            timeOffset = System.currentTimeMillis() - recordingStartTime;
            tool.focusedSendMessage(state.get(), sequence.size(), "操作录制已停止");
        }
    }

    /**
     * 重置记录器
     */
    public void reset() {
        synchronized (state) {
            if (state.get() == ExecutorState.RUNNING || state.get() == ExecutorState.COUNTDOWN) {
                // 如果正在运行或倒计时中，先停止
                pause();
            }
            state.set(ExecutorState.PAUSE);
            sequence.clearOperations();
            timeOffset = 0;
            countdownTask.cancel(true);
            tool.sendMessage(state.get(), sequence.size(), "操作录制已重置");
        }
    }

    /**
     * 监听鼠标事件
     */
    private class MouseListener extends NativeMouseInputAdapter {
        // 动态阈值配置
        private static final int DISTANCE_THRESHOLD = 10; // 初始移动阈值（像素）
        private static final long TIME_WINDOW = 20; // 最小时间间隔（毫秒）
        // 状态跟踪
        private int lastX = Integer.MIN_VALUE; // 上次的位置
        private int lastY = Integer.MIN_VALUE; // 上次的角度
        private long lastRecordTime = 0; // 上次的记录时间
        private double lastAngle = 0; // 上次的角度

        @Override
        public void nativeMouseMoved(NativeMouseEvent e) {
            // 若当前不在录制状态，则直接返回，不处理该事件
            if (state.get() != ExecutorState.RUNNING) return;
            int x = e.getX();
            int y = e.getY();
            long now = System.currentTimeMillis();
            // 首次记录初始化
            if (lastX == Integer.MIN_VALUE) {
                recordMovement(x, y, now, 0);
                return;
            }
            // 计算移动特性
            double distance = Math.hypot(x - lastX, y - lastY); // 移动距离
            double dx = x - lastX;
            double dy = y - lastY;
            double currentAngle = Math.atan2(dy, dx); //移动角度
            // 智能采样条件
            if (shouldRecord(now, distance, x, y, currentAngle)) {
                recordMovement(x, y, now, currentAngle);
            }
        }

        /**
         * 是否应该记录此次移动
         *
         * @param now          当前时间，单位ms
         * @param distance     distance 移动距离
         * @param x            鼠标x坐标
         * @param y            鼠标y坐标
         * @param currentAngle 鼠标目前移动角度，角度制
         * @return 判断结果
         */
        private boolean shouldRecord(long now, double distance, int x, int y, double currentAngle) {
            // 条件1：忽略小于2像素的移动
            if (distance < 2) return false;
            // 条件2：超过时间窗口
            if ((now - lastRecordTime) > calculateDynamicTimeWindow(distance, now)) return true;
            // 条件3：距离超过阈值
            if (distance > DISTANCE_THRESHOLD) return true;
            // 条件4：速度突变（防止快速转向丢失关键点）
            return distance > 5 && calculateAngleDelta(currentAngle, lastAngle) > Math.PI / 4;
        }

        /**
         * 精确计算角度差
         *
         * @param a1 角度1
         * @param a2 角度2
         * @return 角度差
         */
        private double calculateAngleDelta(double a1, double a2) {
            double delta = Math.abs(a1 - a2);
            return Math.min(delta, 2 * Math.PI - delta);
        }

        /**
         * 动态时间窗口计算（根据移动速度调整）
         *
         * @param distance 移动距离
         * @param now      当前时间，单位ms
         * @return 时间窗口
         */
        private long calculateDynamicTimeWindow(double distance, long now) {
            // 计算瞬时速度（像素/毫秒）
            double speed = distance / (now - lastRecordTime);
            // 速度越慢，时间窗口越大
            return (long) (TIME_WINDOW * (1 + 2 / (speed * 1000 + 1)));
        }

        /**
         * 记录鼠标移动事件
         *
         * @param x     鼠标x坐标
         * @param y     鼠标y坐标
         * @param time  当前时间，单位ms
         * @param angle 鼠标目前移动角度，角度制
         */
        private void recordMovement(int x, int y, long time, double angle) {
            MouseMove move = new MouseMove(x, y, getTimestamp());
            sequence.addOperation(move);
            tool.sendMessage(state.get(), sequence.size(), "鼠标移动至坐标[{}, {}]", x, y);
            updateState(x, y, time, angle);
        }

        /**
         * 刷新上次记录状态
         */
        private void updateState(int x, int y, long time, double angle) {
            lastX = x;
            lastY = y;
            lastRecordTime = time;
            lastAngle = angle;
        }

        /**
         * 处理鼠标按键按下事件。当鼠标按键被按下时，如果当前处于录制状态，
         * 则记录该按键按下事件，并将其添加到操作缓冲区，同时向前端发送状态信息。
         *
         * @param e 原生鼠标事件对象，包含鼠标按键按下事件的相关信息
         */
        @Override
        public void nativeMousePressed(NativeMouseEvent e) {
            // 若当前不在录制状态，则直接返回，不处理该事件
            if (state.get() != ExecutorState.RUNNING) return;
            // 根据原生鼠标事件中的按键代码获取对应的鼠标按键枚举值
            MouseCode button = MouseCode.fromNativeCode(e.getButton());
            // 创建一个带时间戳的鼠标按下操作对象
            MousePress mousePress = new MousePress(button, getTimestamp());
            // 向前端发送状态信息，记录鼠标按下的按键，并在后端打印日志
            tool.sendMessage(state.get(), sequence.size(), "鼠标按下按键{}", button);
            // 将鼠标按下操作添加到操作缓冲区，等待后续处理
            sequence.addOperation(mousePress);
        }

        @Override
        public void nativeMouseReleased(NativeMouseEvent e) {
            // 若当前不在录制状态，则直接返回，不处理该事件
            if (state.get() != ExecutorState.RUNNING) return;
            MouseCode button = MouseCode.fromNativeCode(e.getButton());
            MouseRelease release = new MouseRelease(button, getTimestamp());
            tool.sendMessage(state.get(), sequence.size(), "鼠标释放按键{}", button);
            sequence.addOperation(release);
        }

        @Override
        public void nativeMouseDragged(NativeMouseEvent e) {
            nativeMouseMoved(e); // 拖拽视为移动
        }
    }

    /**
     * 滚轮监听事件
     */
    private class MouseWheelListener implements NativeMouseWheelListener {
        private final static long WHEEL_MERGE_THRESHOLD_MS = 50;
        // 优化为基本类型变量（单线程访问无需原子类）
        private long lastWheelTime = 0;
        private int accumulatedUnits = 0;

        /**
         * 鼠标滚轮监听, 优化了滚动记录逻辑
         */
        @Override
        public void nativeMouseWheelMoved(NativeMouseWheelEvent e) {
            // 若当前不在录制状态，则直接返回，不处理该事件
            if (state.get() != ExecutorState.RUNNING) return;

            // 单次获取时间和单位（减少重复调用）
            final int units = -e.getWheelRotation();
            final long currentTime = System.currentTimeMillis();

            // 优化判断逻辑顺序
            final boolean shouldCommit = (currentTime - lastWheelTime > WHEEL_MERGE_THRESHOLD_MS)
                    || (accumulatedUnits != 0 && !sameSign(accumulatedUnits, units));

            if (shouldCommit) {
                // 提交已合并的操作（如果有累计值）
                if (accumulatedUnits != 0) {
                    final MouseWheelScroll scroll = new MouseWheelScroll(accumulatedUnits, getTimestamp());
                    tool.sendMessage(state.get(), sequence.size(), "鼠标滚轮滚动{}单位", accumulatedUnits);
                    sequence.addOperation(scroll);
                }
                // 重置状态（保留当前units作为新起点）
                accumulatedUnits = units;
                lastWheelTime = currentTime;
            } else accumulatedUnits += units;// 合并滚动单位
        }

        // 符号判断优化（位运算）
        private boolean sameSign(int a, int b) {
            return (a ^ b) >= 0;
        }
    }

    /**
     * 监听按键事件
     */
    private class KeyListener extends NativeKeyAdapter {
        @Override
        public void nativeKeyPressed(NativeKeyEvent e) {
            // 若当前不在录制状态，则直接返回，不处理该事件
            if (state.get() != ExecutorState.RUNNING) return;
            KeyCode keyCode = KeyCode.fromNativeEvent(e);
            KeyPress keyPress = new KeyPress(keyCode, getTimestamp());
            if (keyCode.getNativeCode() == repository.getHotkey("record.toggle")) return;
            if (keyCode.getNativeCode() == repository.getHotkey("record.run.toggle")) {
                tool.sendMessage(state.get(), sequence.size(), "键盘按下按键{}, 但是该按键是运行切换快捷键无法录入", keyCode.name());
            } else {
                tool.sendMessage(state.get(), sequence.size(), "键盘按下按键{}", keyCode.name());
                sequence.addOperation(keyPress);
            }
        }

        @Override
        public void nativeKeyReleased(NativeKeyEvent e) {
            // 若当前不在录制状态，则直接返回，不处理该事件
            if (state.get() != ExecutorState.RUNNING) return;
            KeyCode keyCode = KeyCode.fromNativeEvent(e);
            KeyRelease keyRelease = new KeyRelease(keyCode, getTimestamp());
            if (keyCode.getNativeCode() == repository.getHotkey("record.toggle")) return;
            if (keyCode.getNativeCode() == repository.getHotkey("record.run.toggle")) {
                tool.sendMessage(state.get(), sequence.size(),
                        "键盘按下按键{}, 但是该按键是运行切换快捷键无法录入", keyCode.name());
            } else {
                tool.sendMessage(state.get(), sequence.size(), "键盘释放按键{}", keyCode.name());
                sequence.addOperation(keyRelease);
            }
        }
    }

}